"""AI Extraction Service - Hybrid architecture for property matching."""

from __future__ import annotations

import hashlib
import json
import logging
import re
from functools import lru_cache
from typing import TYPE_CHECKING

from ..schemas.ai_extraction import LeadRequirements, PropertyFeatures

if TYPE_CHECKING:
    from ..models.lead import Lead
    from ..models.property import Property

logger = logging.getLogger(__name__)


class AIExtractionService:
    """AI增强提取服务 (混合架构: 规则引擎 + AI语义分析)."""

    def __init__(self, qwen_api_key: str | None = None):
        """
        初始化AI提取服务.

        Args:
            qwen_api_key: Qwen API密钥
        """
        self.qwen_api_key = qwen_api_key
        # 实例级缓存 (线程安全 - 每个实例独立)
        self._lead_cache: dict[str, LeadRequirements] = {}
        self._property_cache: dict[str, PropertyFeatures] = {}

    # ============================================
    # Lead Requirements Extraction
    # ============================================

    def extract_lead_requirements(self, lead: Lead) -> LeadRequirements:
        """
        提取客户需求 (混合策略: 规则 + AI).

        Args:
            lead: 客户对象

        Returns:
            LeadRequirements: 结构化需求
        """
        # 缓存键
        cache_key = f"lead_req_{lead.id}_{hash(lead.notes or '')}"
        if cache_key in self._lead_cache:
            return self._lead_cache[cache_key]

        # 1. 规则引擎提取 (Fast Path)
        rule_result = self._extract_lead_by_rules(lead)

        # 2. 判断是否需要AI
        if self._should_use_ai_for_lead(lead, rule_result):
            try:
                # 3. AI增强提取
                ai_result = self._extract_lead_by_ai(lead)
                # 4. 合并结果
                merged = self._merge_lead_results(rule_result, ai_result)
                merged.extraction_method = "hybrid"
            except Exception as e:
                logger.warning(f"AI extraction failed for lead {lead.id}: {e}, using rules only")
                merged = rule_result
                merged.extraction_method = "rules"
        else:
            merged = rule_result
            merged.extraction_method = "rules"

        # 缓存结果
        self._lead_cache[cache_key] = merged
        return merged

    def _extract_lead_by_rules(self, lead: Lead) -> LeadRequirements:
        """规则引擎提取 (Fast Path)."""
        notes = lead.notes or ""

        # 预算提取 (从 budget_range 字段)
        budget_min, budget_max = None, None
        if lead.budget_range and "-" in lead.budget_range:
            try:
                parts = lead.budget_range.replace("元", "").replace(",", "").split("-")
                budget_min = int(parts[0].strip())
                budget_max = int(parts[1].strip())
            except Exception as e:
                logger.debug(f"Failed to parse budget_range '{lead.budget_range}': {e}")

        # 户型提取 (正则匹配)
        layout = None
        layout_pattern = r"(\d+[室房][\d一二三四五]*[厅]?)"
        if match := re.search(layout_pattern, notes):
            layout = match.group(1)

        # 区域提取 (关键词匹配)
        area_preferences = []
        common_areas = [
            "朝阳",
            "海淀",
            "东城",
            "西城",
            "丰台",
            "石景山",
            "望京",
            "国贸",
            "中关村",
            "CBD",
            "三里屯",
            "亚运村",
        ]
        for area in common_areas:
            if area in notes:
                area_preferences.append(area)

        # 标签提取 (从 tag_names 字段)
        tags = (lead.tag_names or "").split(",") if lead.tag_names else []
        tags = [t.strip() for t in tags if t.strip()]

        return LeadRequirements(
            layout=layout,
            area_preferences=area_preferences,
            budget_min=budget_min,
            budget_max=budget_max,
            must_have_tags=tags,  # 默认都视为必须项
            nice_to_have_tags=[],
            commute_location=None,
            commute_max_minutes=None,
            budget_flexibility="normal",
            urgency="normal",
            extraction_method="rules",
            confidence=0.7,
            ai_raw_response=None,
        )

    def _should_use_ai_for_lead(self, lead: Lead, rule_result: LeadRequirements) -> bool:
        """判断是否需要AI增强."""
        notes = lead.notes or ""

        # 无API密钥
        if not self.qwen_api_key:
            return False

        # 备注为空或很短 → 不需要AI
        if len(notes) < 10:
            return False

        # 规则已提取充分信息 → 不需要AI
        if (
            rule_result.layout
            and rule_result.area_preferences
            and len(rule_result.must_have_tags) >= 2
        ):
            return False

        # 包含复杂需求关键词 → 需要AI
        ai_triggers = ["通勤", "上班", "工作", "希望", "最好", "要求", "不要", "附近"]
        if any(trigger in notes for trigger in ai_triggers):
            return True

        # 备注较长但规则提取不足 → 需要AI
        if len(notes) > 50 and not rule_result.layout:
            return True

        return False

    def _extract_lead_by_ai(self, lead: Lead) -> LeadRequirements:
        """AI语义提取 (Smart Path)."""
        prompt = self._build_lead_extraction_prompt(lead)

        # 调用Qwen API
        response = self._call_qwen_api(prompt)

        # 解析JSON响应
        try:
            data = json.loads(response)
        except Exception as e:
            # 尝试提取JSON (有时AI会包裹在markdown中)
            logger.debug(f"Initial JSON parse failed: {e}, attempting extraction from markdown")
            json_match = re.search(r"\{.*\}", response, re.DOTALL)
            if json_match:
                data = json.loads(json_match.group())
            else:
                logger.error(f"AI response is not valid JSON. Response: {response[:200]}")
                raise ValueError("AI response is not valid JSON")

        return LeadRequirements(
            layout=data.get("layout"),
            area_preferences=data.get("area_preferences") or [],
            budget_min=data.get("budget_min"),
            budget_max=data.get("budget_max"),
            commute_location=data.get("commute_location"),
            commute_max_minutes=data.get("commute_max_minutes"),
            must_have_tags=data.get("must_have_tags") or [],
            nice_to_have_tags=data.get("nice_to_have_tags") or [],
            budget_flexibility=data.get("budget_flexibility", "normal"),
            urgency=data.get("urgency", "normal"),
            extraction_method="ai",
            confidence=0.9,
            ai_raw_response=response,
        )

    def _build_lead_extraction_prompt(self, lead: Lead) -> str:
        """构建客户需求提取Prompt."""
        return f"""你是房产CRM系统的智能助手。请分析客户需求,提取结构化信息。

客户信息:
- 姓名: {lead.name}
- 需求类型: {lead.demand_type.value}
- 预算范围: {lead.budget_range or "未明确"}
- 已有标签: {lead.tag_names or "无"}
- 备注: {lead.notes or "无"}

请提取以下信息,返回JSON格式(只返回JSON,无其他内容):
{{
  "layout": "户型需求(如: 2室1厅, 三室两厅, null表示无)",
  "area_preferences": ["区域偏好列表(如: ['望京','国贸'])"],
  "budget_min": 预算下限(整数,元/月, null表示无),
  "budget_max": 预算上限(整数,元/月, null表示无),
  "commute_location": "通勤地点(从'上班在XX'推断, null表示无)",
  "commute_max_minutes": 通勤时间要求(整数,分钟, null表示无),
  "must_have_tags": ["必须满足的需求(如: ['近地铁','可养宠物'])"],
  "nice_to_have_tags": ["加分项需求(如: ['有电梯','朝南'])"],
  "budget_flexibility": "strict|normal|flexible (判断预算灵活度)",
  "urgency": "urgent|normal|flexible (判断紧迫程度)"
}}

提取规则:
1. 从备注中推断隐含需求(如"上班在国贸"→commute_location="国贸")
2. 区分必须项和加分项(出现"必须""一定"的是must_have)
3. 标签具体化(如"交通方便"→"近地铁")
4. 户型支持自然语言(如"两室一厅"→"2室1厅")
5. 如果信息不存在,使用null
"""

    def _merge_lead_results(
        self,
        rule_result: LeadRequirements,
        ai_result: LeadRequirements,
    ) -> LeadRequirements:
        """合并规则和AI结果 (AI优先,规则补充)."""
        return LeadRequirements(
            layout=ai_result.layout or rule_result.layout,
            area_preferences=list(set(ai_result.area_preferences + rule_result.area_preferences)),
            budget_min=ai_result.budget_min or rule_result.budget_min,
            budget_max=ai_result.budget_max or rule_result.budget_max,
            commute_location=ai_result.commute_location,  # AI独有
            commute_max_minutes=ai_result.commute_max_minutes,  # AI独有
            must_have_tags=list(set(ai_result.must_have_tags + rule_result.must_have_tags)),
            nice_to_have_tags=ai_result.nice_to_have_tags,
            budget_flexibility=ai_result.budget_flexibility,
            urgency=ai_result.urgency,
            extraction_method="hybrid",
            confidence=0.85,
            ai_raw_response=ai_result.ai_raw_response,
        )

    # ============================================
    # Property Features Extraction
    # ============================================

    def extract_property_features(self, property: Property) -> PropertyFeatures:
        """
        提取房源特征 (混合策略: 规则 + AI).

        Args:
            property: 房源对象

        Returns:
            PropertyFeatures: 结构化特征
        """
        cache_key = f"prop_feat_{property.id}_{hash(property.notes or '')}"
        if cache_key in self._property_cache:
            return self._property_cache[cache_key]

        # 1. 规则提取
        rule_result = self._extract_property_by_rules(property)

        # 2. AI增强 (如果备注丰富)
        if self._should_use_ai_for_property(property, rule_result):
            try:
                ai_result = self._extract_property_by_ai(property)
                merged = self._merge_property_results(rule_result, ai_result)
                merged.extraction_method = "hybrid"
            except Exception as e:
                logger.warning(f"AI extraction failed for property {property.id}: {e}")
                merged = rule_result
                merged.extraction_method = "rules"
        else:
            merged = rule_result
            merged.extraction_method = "rules"

        self._property_cache[cache_key] = merged
        return merged

    def _extract_property_by_rules(self, property: Property) -> PropertyFeatures:
        """规则引擎提取房源特征."""
        notes = property.notes or ""

        # 设施关键词匹配
        facilities = []
        facility_keywords = {
            "地铁": "近地铁",
            "停车": "有车位",
            "电梯": "有电梯",
            "宠物": "可养宠物",
            "家具": "有家具",
            "家电": "有家电",
        }
        for keyword, tag in facility_keywords.items():
            if keyword in notes:
                facilities.append(tag)

        # 地标提取 (基础版)
        landmarks = []
        if "地铁" in notes:
            landmarks.append("地铁站")

        return PropertyFeatures(
            facilities=facilities,
            nearby_landmarks=landmarks,
            nearby_business_districts=[],
            highlights=[],
            suitable_for=[],
            extraction_method="rules",
            confidence=0.6,
            ai_raw_response=None,
        )

    def _should_use_ai_for_property(
        self, property: Property, rule_result: PropertyFeatures
    ) -> bool:
        """判断房源是否需要AI增强."""
        notes = property.notes or ""

        # 无API密钥
        if not self.qwen_api_key:
            return False

        # 备注丰富 → 需要AI
        if len(notes) > 50:
            return True

        # 规则提取不足 → 需要AI
        if len(rule_result.facilities) < 2:
            return True

        return False

    def _extract_property_by_ai(self, property: Property) -> PropertyFeatures:
        """AI提取房源特征."""
        prompt = self._build_property_extraction_prompt(property)
        response = self._call_qwen_api(prompt)

        try:
            data = json.loads(response)
        except Exception as e:
            logger.debug(f"Property JSON parse failed: {e}, attempting extraction")
            json_match = re.search(r"\{.*\}", response, re.DOTALL)
            if json_match:
                data = json.loads(json_match.group())
            else:
                logger.error(f"Property AI response is not valid JSON. Response: {response[:200]}")
                raise ValueError("AI response is not valid JSON")

        return PropertyFeatures(
            facilities=data.get("facilities") or [],
            nearby_landmarks=data.get("nearby_landmarks") or [],
            nearby_business_districts=data.get("nearby_business_districts") or [],
            highlights=data.get("highlights") or [],
            suitable_for=data.get("suitable_for") or [],
            extraction_method="ai",
            confidence=0.9,
            ai_raw_response=response,
        )

    def _build_property_extraction_prompt(self, property: Property) -> str:
        """构建房源特征提取Prompt."""
        return f"""你是房产CRM系统的智能助手。请分析房源信息,提取结构化特征。

房源信息:
- 小区: {property.community_name}
- 地址: {property.address}
- 户型: {property.layout}
- 面积: {property.area}㎡
- 朝向: {property.orientation or "未知"}
- 楼层: {property.floor_info or "未知"}
- 租金: {property.rent_price}元/月
- 备注: {property.notes or "无"}

请提取以下信息,返回JSON格式(只返回JSON,无其他内容):
{{
  "facilities": ["配套设施(具体化,如: '地铁站500米','有电梯','允许养宠物')"],
  "nearby_landmarks": ["周边地标(如: '望京SOHO','14号线望京站','清华大学')"],
  "nearby_business_districts": ["邻近商圈(如: ['CBD','国贸','中关村'] - 用于通勤匹配)"],
  "highlights": ["房源亮点(如: '采光好','安静','精装修','南北通透')"],
  "suitable_for": ["适合人群(如: ['上班族','家庭','学生'])"]
}}

提取规则:
1. facilities要具体化(如"地铁站500米"而非"交通便利")
2. 从地址推断nearby_landmarks和nearby_business_districts
3. 从朝向/楼层/装修推断highlights
4. 从户型/面积/价格推断suitable_for
5. 如果信息不存在,使用空数组[]
"""

    def _merge_property_results(
        self,
        rule_result: PropertyFeatures,
        ai_result: PropertyFeatures,
    ) -> PropertyFeatures:
        """合并规则和AI结果."""
        return PropertyFeatures(
            facilities=list(set(ai_result.facilities + rule_result.facilities)),
            nearby_landmarks=list(set(ai_result.nearby_landmarks + rule_result.nearby_landmarks)),
            nearby_business_districts=ai_result.nearby_business_districts,  # AI独有
            highlights=ai_result.highlights,  # AI独有
            suitable_for=ai_result.suitable_for,  # AI独有
            extraction_method="hybrid",
            confidence=0.85,
            ai_raw_response=ai_result.ai_raw_response,
        )

    # ============================================
    # Semantic Similarity (语义相似度)
    # ============================================

    @lru_cache(maxsize=1000)
    def semantic_similarity(self, text1: str, text2: str) -> float:
        """
        计算两个文本的语义相似度 (0-1).

        Args:
            text1: 文本1
            text2: 文本2

        Returns:
            float: 相似度分数 (0-1)
        """
        # 快速路径: 完全相同
        if text1 == text2:
            return 1.0

        # 快速路径: 包含关系
        if text1 in text2 or text2 in text1:
            return 0.9

        # 无AI → 降级到字符串相似度
        if not self.qwen_api_key:
            from difflib import SequenceMatcher

            return SequenceMatcher(None, text1, text2).ratio()

        # AI语义相似度
        prompt = f"""判断以下两个地点/需求是否相似或相关,返回相似度分数(0-1之间的小数)。

文本1: {text1}
文本2: {text2}

返回格式: 只返回一个0-1之间的数字,如: 0.85

评分标准:
- 1.0: 完全相同
- 0.9: 同义词或包含关系(如"望京"vs"望京SOHO")
- 0.8: 同一区域(如"国贸"vs"CBD")
- 0.6: 相关但不同(如"朝阳区"vs"望京")
- 0.3: 较远的关联
- 0.0: 完全无关
"""

        try:
            response = self._call_qwen_api(prompt)
            score = float(response.strip())
            return max(0.0, min(1.0, score))  # 限制在0-1
        except Exception as e:
            # 降级到字符串相似度
            logger.debug(f"Semantic similarity AI call failed: {e}, using fallback")
            from difflib import SequenceMatcher

            return SequenceMatcher(None, text1, text2).ratio()

    # ============================================
    # Visitor Sheet Extraction (Phase 2)
    # ============================================

    def extract_visitor_sheet_info(self, notes: str, community_name: str | None = None) -> dict:
        """
        从带看情况备注中提取访客单信息（Phase 2: 访客单快捷模式）

        Args:
            notes: 带看情况备注文本
            community_name: 小区名称（用于匹配房源）

        Returns:
            {
                "satisfaction": 4,  # 1-5星
                "intention": "high",  # high/medium/low
                "pros_tags": "采光好,户型方正,交通便利",
                "cons_tags": "楼层略高,租金偏贵",
                "visit_time": "14:00",  # HH:MM 格式
                "duration_minutes": 45,
                "matched_community": "望京SOHO",  # 如果备注中提到小区名
                "confidence": 0.85
            }
        """
        if not notes or len(notes) < 10:
            return {"confidence": 0.0}

        # 1. 规则引擎提取（快速路径）
        rule_result = self._extract_visitor_sheet_by_rules(notes)

        # 2. 判断是否需要AI增强
        if self._should_use_ai_for_visitor_sheet(notes, rule_result):
            try:
                ai_result = self._extract_visitor_sheet_by_ai(notes, community_name)
                # 合并结果（AI优先，规则补充）
                merged = {**rule_result, **ai_result}
                merged["extraction_method"] = "hybrid"
                merged["confidence"] = 0.85
                return merged
            except Exception as e:
                logger.warning(f"Visitor sheet AI extraction failed: {e}, using rules only")
                rule_result["extraction_method"] = "rules"
                return rule_result
        else:
            rule_result["extraction_method"] = "rules"
            return rule_result

    def _extract_visitor_sheet_by_rules(self, notes: str) -> dict:
        """规则引擎提取访客单信息"""
        result: dict = {}

        # 1. 满意度提取（情感分析关键词）
        positive_keywords = ["满意", "喜欢", "不错", "很好", "非常好", "棒", "赞", "完美"]
        negative_keywords = ["不满意", "不喜欢", "差", "不好", "失望", "糟糕"]

        positive_count = sum(1 for kw in positive_keywords if kw in notes)
        negative_count = sum(1 for kw in negative_keywords if kw in notes)

        if positive_count > negative_count:
            if positive_count >= 3:
                result["satisfaction"] = 5
            elif positive_count >= 2:
                result["satisfaction"] = 4
            else:
                result["satisfaction"] = 3
        elif negative_count > positive_count:
            if negative_count >= 2:
                result["satisfaction"] = 1
            else:
                result["satisfaction"] = 2
        else:
            result["satisfaction"] = 3  # 中性

        # 2. 意向度提取（关键词匹配）
        high_intention_keywords = ["想租", "决定", "立刻", "马上", "尽快", "签约"]
        medium_intention_keywords = ["考虑", "再看看", "比较", "犹豫"]
        low_intention_keywords = ["不合适", "再说", "暂时不", "不考虑"]

        if any(kw in notes for kw in high_intention_keywords):
            result["intention"] = "high"
        elif any(kw in notes for kw in low_intention_keywords):
            result["intention"] = "low"
        elif any(kw in notes for kw in medium_intention_keywords):
            result["intention"] = "medium"
        else:
            # 根据满意度推断
            satisfaction = result.get("satisfaction", 3)
            if satisfaction >= 4:
                result["intention"] = "high"
            elif satisfaction <= 2:
                result["intention"] = "low"
            else:
                result["intention"] = "medium"

        # 3. 优点标签提取
        pros_patterns = [
            ("采光", "采光好"),
            ("通风", "通风好"),
            ("户型", "户型方正"),
            ("交通", "交通便利"),
            ("地铁", "近地铁"),
            ("安静", "环境安静"),
            ("新", "装修新"),
            ("家具", "家具齐全"),
            ("楼层", "楼层适中"),
        ]
        pros = []
        for keyword, tag in pros_patterns:
            if keyword in notes and ("好" in notes or "不错" in notes or tag in notes):
                pros.append(tag)

        if pros:
            result["pros_tags"] = ",".join(pros)

        # 4. 缺点标签提取
        cons_patterns = [
            ("贵", "租金偏贵"),
            ("小", "面积较小"),
            ("旧", "装修老旧"),
            ("暗", "采光不足"),
            ("吵", "噪音较大"),
            ("远", "位置较远"),
            ("高", "楼层过高"),
            ("低", "楼层过低"),
        ]
        cons = []
        for keyword, tag in cons_patterns:
            if keyword in notes:
                cons.append(tag)

        if cons:
            result["cons_tags"] = ",".join(cons)

        # 5. 时间提取
        time_pattern = r"(\d{1,2})[点:：](\d{0,2})"
        if match := re.search(time_pattern, notes):
            hour = int(match.group(1))
            minute = int(match.group(2)) if match.group(2) else 0

            # 处理上午/下午
            if "下午" in notes or "晚上" in notes:
                if hour < 12:
                    hour += 12
            elif "早上" in notes or "上午" in notes:
                if hour == 12:
                    hour = 0

            if 0 <= hour <= 23 and 0 <= minute <= 59:
                result["visit_time"] = f"{hour:02d}:{minute:02d}"

        # 6. 停留时长提取
        duration_pattern = r"(\d+)\s*分钟"
        if match := re.search(duration_pattern, notes):
            result["duration_minutes"] = int(match.group(1))

        result["confidence"] = 0.65
        return result

    def _should_use_ai_for_visitor_sheet(self, notes: str, rule_result: dict) -> bool:
        """判断是否需要AI增强访客单提取"""
        # 无API密钥
        if not self.qwen_api_key:
            return False

        # 备注较短
        if len(notes) < 20:
            return False

        # 规则已提取充分
        extracted_fields = sum(1 for k in ["satisfaction", "intention", "pros_tags", "cons_tags"]
                             if k in rule_result and rule_result[k])
        if extracted_fields >= 3:
            return False

        # 备注丰富但规则提取不足 → 需要AI
        if len(notes) > 50 and extracted_fields < 2:
            return True

        return False

    def _extract_visitor_sheet_by_ai(self, notes: str, community_name: str | None = None) -> dict:
        """AI提取访客单信息"""
        prompt = self._build_visitor_sheet_extraction_prompt(notes, community_name)
        response = self._call_qwen_api(prompt)

        try:
            data = json.loads(response)
        except Exception:
            # 尝试提取JSON
            json_match = re.search(r"\{.*\}", response, re.DOTALL)
            if json_match:
                data = json.loads(json_match.group())
            else:
                logger.error(f"Visitor sheet AI response not JSON: {response[:200]}")
                raise ValueError("AI response is not valid JSON")

        # 转换和验证数据
        result: dict = {}

        if "satisfaction" in data and 1 <= data["satisfaction"] <= 5:
            result["satisfaction"] = data["satisfaction"]

        if "intention" in data and data["intention"] in ["high", "medium", "low"]:
            result["intention"] = data["intention"]

        if "pros_tags" in data and data["pros_tags"]:
            if isinstance(data["pros_tags"], list):
                result["pros_tags"] = ",".join(data["pros_tags"])
            else:
                result["pros_tags"] = data["pros_tags"]

        if "cons_tags" in data and data["cons_tags"]:
            if isinstance(data["cons_tags"], list):
                result["cons_tags"] = ",".join(data["cons_tags"])
            else:
                result["cons_tags"] = data["cons_tags"]

        if "visit_time" in data and data["visit_time"]:
            result["visit_time"] = data["visit_time"]

        if "duration_minutes" in data and isinstance(data["duration_minutes"], int):
            result["duration_minutes"] = data["duration_minutes"]

        if "matched_community" in data and data["matched_community"]:
            result["matched_community"] = data["matched_community"]

        result["ai_raw_response"] = response
        result["confidence"] = 0.9
        return result

    def _build_visitor_sheet_extraction_prompt(self, notes: str, community_name: str | None) -> str:
        """构建访客单信息提取Prompt"""
        community_hint = f"\n已知小区名称: {community_name}" if community_name else ""

        return f"""你是房产CRM系统的智能助手。请分析带看情况备注，提取访客单信息。
{community_hint}

带看情况备注:
{notes}

请提取以下信息，返回JSON格式（只返回JSON，无其他内容）：
{{
  "satisfaction": 满意度评分(1-5星整数, 根据情感倾向判断),
  "intention": "意向度(high/medium/low, 根据租房意愿判断)",
  "pros_tags": ["优点标签数组(如: ['采光好','户型方正','交通便利'])"],
  "cons_tags": ["缺点标签数组(如: ['租金偏贵','楼层过高'])"],
  "visit_time": "带看时间(HH:MM格式, 如: '14:30', 如无则null)",
  "duration_minutes": 停留时长(整数分钟数, 如无则null),
  "matched_community": "小区名称(如果备注中提到, 否则null)"
}}

提取规则:
1. satisfaction: 根据情感词判断(满意/喜欢→4-5星, 中性→3星, 不满意→1-2星)
2. intention: 高意向(想租/决定)、中意向(考虑/比较)、低意向(不合适)
3. pros_tags: 具体化优点(如"采光好"而非"不错")
4. cons_tags: 具体化缺点(如"租金偏贵"而非"有点贵")
5. visit_time: 从"下午2点"提取为"14:00"
6. duration_minutes: 从"看了半小时"提取为30
7. 如果信息不存在，使用null
"""

    # ============================================
    # Lightweight Extraction (For Form Auto-Fill)
    # ============================================

    def extract_from_notes_light(self, notes: str, existing_data: dict | None = None) -> dict:
        """
        轻量级提取：从备注中提取基础字段，用于传统表单智能补全

        Args:
            notes: 备注文本
            existing_data: 已有数据（避免覆盖用户手动输入）

        Returns:
            {
                "budget_range": "7000-8000",
                "demand_type": "rent_whole",
                "area_preferences": ["朝阳区"],
                "layout": "2室1厅"
            }
        """
        if not notes or len(notes) < 10:
            return {}

        existing_data = existing_data or {}
        result = {}

        # 1. 预算范围提取（规则引擎）
        if not existing_data.get("budget_range"):
            budget_pattern = r"(\d{3,5})\s*[-~到至]\s*(\d{3,5})"
            if match := re.search(budget_pattern, notes):
                budget_min = match.group(1)
                budget_max = match.group(2)
                result["budget_range"] = f"{budget_min}-{budget_max}"

        # 2. 户型提取（规则引擎）
        if not existing_data.get("layout"):
            layout_pattern = r"([一二三四五1-5][室房]([一二三四五1-5]厅)?|[一二三四五1-5]居)"
            if match := re.search(layout_pattern, notes):
                layout_raw = match.group(0)
                # 标准化：一室 → 1室1厅
                layout_normalized = layout_raw.replace("一", "1").replace("二", "2")\
                    .replace("三", "3").replace("四", "4").replace("五", "5")
                if "厅" not in layout_normalized and "室" in layout_normalized:
                    layout_normalized = layout_normalized.replace("室", "室1厅")
                result["layout"] = layout_normalized

        # 3. 区域偏好提取（关键词匹配）
        area_keywords = [
            "朝阳", "海淀", "东城", "西城", "丰台", "石景山",
            "望京", "国贸", "中关村", "CBD", "三里屯", "亚运村"
        ]
        areas_found = [area for area in area_keywords if area in notes]
        if areas_found:
            result["area_preferences"] = areas_found

        # 4. 需求类型推断（关键词匹配）
        if not existing_data.get("demand_type"):
            if "整租" in notes or "单独" in notes:
                result["demand_type"] = "rent_whole"
            elif "合租" in notes or "室友" in notes or "分租" in notes:
                result["demand_type"] = "rent_share"
            elif "短租" in notes or "临时" in notes or "月租" in notes and "一个月" in notes:
                result["demand_type"] = "rent_short"
            elif "买房" in notes or "购房" in notes or "买" in notes and ("房" in notes or "万" in notes):
                result["demand_type"] = "buy"
            elif "卖房" in notes or "出售" in notes:
                result["demand_type"] = "sell"
            else:
                # 默认推断为整租（最常见）
                result["demand_type"] = "rent_whole"

        # 5. 如果提取不足且有 API Key，调用 AI 增强
        if self.qwen_api_key and len(notes) > 30 and len(result) < 2:
            try:
                ai_enhanced = self._extract_from_notes_by_ai(notes, existing_data)
                # 合并结果（AI 优先）
                result.update(ai_enhanced)
            except Exception as e:
                logger.warning(f"AI lightweight extraction failed: {e}, using rules only")

        return result

    def _extract_from_notes_by_ai(self, notes: str, existing_data: dict | None = None) -> dict:
        """AI 轻量级提取（用于表单补全）"""
        existing_data = existing_data or {}

        prompt = f"""从以下客户备注中提取关键信息，用于自动填充表单字段。

备注内容：
{notes}

已有字段（不要覆盖）：
{json.dumps(existing_data, ensure_ascii=False)}

请提取以下字段（如果备注中未提及则返回 null）：
1. budget_range: 预算范围（格式：最低-最高，如 "7000-8000"）
2. demand_type: 需求类型（rent_whole/rent_share/rent_short/buy/sell）
3. layout: 户型（如 "2室1厅"）
4. area_preferences: 区域偏好（数组，如 ["朝阳区", "望京"]）

返回 JSON 格式（只返回 JSON，无其他内容）：
{{
  "budget_range": "7000-8000",
  "demand_type": "rent_whole",
  "layout": "2室1厅",
  "area_preferences": ["朝阳区"]
}}
"""

        response = self._call_qwen_api(prompt)

        try:
            data = json.loads(response)
        except Exception:
            # 尝试提取 JSON
            json_match = re.search(r"\{.*\}", response, re.DOTALL)
            if json_match:
                data = json.loads(json_match.group())
            else:
                return {}

        # 过滤掉 null 值和已存在的字段
        result = {}
        for key, value in data.items():
            if value is not None and not existing_data.get(key):
                result[key] = value

        return result

    # ============================================
    # Qwen API Integration
    # ============================================

    def _call_qwen_api(self, prompt: str) -> str:
        """
        调用Qwen API.

        Args:
            prompt: 提示词

        Returns:
            str: AI响应

        Raises:
            RuntimeError: API调用失败
        """
        if not self.qwen_api_key:
            raise RuntimeError("Qwen API key not configured")

        try:
            import dashscope
            from http import HTTPStatus
        except ImportError:
            raise RuntimeError("dashscope library not installed. Run: pip install dashscope")

        try:
            # 配置API Key
            dashscope.api_key = self.qwen_api_key

            # 调用Qwen API
            response = dashscope.Generation.call(
                model="qwen-turbo",
                messages=[{"role": "user", "content": prompt}],
                result_format="message",
                temperature=0.1,  # 低温度保证稳定性
                top_p=0.8,
                max_tokens=1000,
            )

            # 检查响应状态
            if hasattr(response, "status_code") and response.status_code == HTTPStatus.OK:
                ai_text = response.output.choices[0]["message"]["content"]
                return ai_text  # type: ignore[no-any-return]
            elif hasattr(response, "code"):
                raise RuntimeError(f"Qwen API error: {response.code} - {response.message}")
            else:
                raise RuntimeError("Qwen API returned unexpected response format")

        except Exception as e:
            # Log the full error for debugging
            logger.error(f"Qwen API call failed: {type(e).__name__}: {str(e)}")
            raise RuntimeError(f"Qwen API call failed: {str(e)}")
